﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Silverlight.Navigation.Favorites;
using Silverlight.Navigation.History;
using Silverlight.Navigation.Mapping;

namespace Silverlight.Navigation.Controls
{
    public partial class Browser : UserControl
    {
        #region Fields

        private FavoritesManager _favoritesManager;
        private HistoryManager _historyManager;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Browser class.
        /// </summary>
        public Browser()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Initializes a new instances of the Browser class.
        /// </summary>
        /// <param name="applicationMap">The application map for the control to use.</param>
        /// <param name="historyManager">The object used to manage the application's history.</param>
        public Browser(ApplicationMap applicationMap, HistoryManager historyManager, FavoritesManager favoritesManager)
            : this()
        {
            if (applicationMap == null)
                throw new ArgumentNullException("applicationMap");

            if (historyManager == null)
                throw new ArgumentNullException("historyManager");

            if (favoritesManager == null)
                throw new ArgumentNullException("favoritesManager");

            this.ApplicationMap = applicationMap;
            this.HistoryManager = historyManager;
            this.FavoritesManager = favoritesManager;
        }

        #endregion

        #region Properties

        /// <summary>
        /// The application map used by the control.
        /// </summary>
        public ApplicationMap ApplicationMap { get; set; }

        #region DisplayApplicationPath Property

        /// <summary>
        /// When to display the application path (dependency property).
        /// </summary>
        public static readonly DependencyProperty DisplayApplicationPathProperty = DependencyProperty.Register(
            "DisplayApplicationPath",
            typeof(DisplayNavigationItem),
            typeof(Browser),
            new PropertyMetadata(DisplayNavigationItem.Always));

        /// <summary>
        /// When to display the application path.
        /// </summary>
        public DisplayNavigationItem DisplayApplicationPath
        {
            get { return (DisplayNavigationItem)GetValue(DisplayApplicationPathProperty); }
            set { SetValue(DisplayApplicationPathProperty, value); }
        }

        #endregion

        #region DisplayMenu Property

        /// <summary>
        /// When to display the menu (dependency property).
        /// </summary>
        public static readonly DependencyProperty DisplayMenuProperty = DependencyProperty.Register(
            "DisplayMenu",
            typeof(DisplayNavigationItem),
            typeof(Browser),
            new PropertyMetadata(DisplayNavigationItem.Always));

        /// <summary>
        /// When to display the menu.
        /// </summary>
        public DisplayNavigationItem DisplayMenu
        {
            get { return (DisplayNavigationItem)GetValue(DisplayMenuProperty); }
            set { SetValue(DisplayMenuProperty, value); }
        }

        #endregion

        #region DisplayNavigationToolbar Property

        /// <summary>
        /// When to display the navigation toolbar (dependency property).
        /// </summary>
        public static readonly DependencyProperty DisplayNavigationToolbarProperty = DependencyProperty.Register(
            "DisplayNavigationToolbar",
            typeof(DisplayNavigationItem),
            typeof(Browser),
            new PropertyMetadata(DisplayNavigationItem.Always));

        /// <summary>
        /// When to display the navigation toolbar.
        /// </summary>
        public DisplayNavigationItem DisplayNavigationToolbar
        {
            get { return (DisplayNavigationItem)GetValue(DisplayNavigationToolbarProperty); }
            set { SetValue(DisplayNavigationToolbarProperty, value); }
        }

        #endregion

        /// <summary>
        /// The object used to manage the application's history.
        /// </summary>
        public HistoryManager HistoryManager 
        {
            get { return _historyManager; }
            set
            {
                _historyManager = value;
                this.NavigationToolbar.HistoryManager = _historyManager;
            }
        }

        /// <summary>
        /// The object used to manage the application's favorites.
        /// </summary>
        public FavoritesManager FavoritesManager
        {
            get { return _favoritesManager; }
            set
            {
                _favoritesManager = value;
                this.NavigationToolbar.FavoritesManager = _favoritesManager;
            }
        }

        #region IsNavigationToolbarExpanded Property

        /// <summary>
        /// Whether or not the navigation toolbar is expanded (dependency property).
        /// </summary>
        public static readonly DependencyProperty IsNavigationToolbarExpandedProperty = DependencyProperty.Register(
            "IsNavigationToolbarExpanded",
            typeof(bool),
            typeof(Browser),
            new PropertyMetadata(true));

        /// <summary>
        /// Whethe ror not the navigation toolbar is expanded.
        /// </summary>
        public bool IsNavigationToolbarExpanded
        {
            get { return (bool)GetValue(IsNavigationToolbarExpandedProperty); }
            set { SetValue(IsNavigationToolbarExpandedProperty, value); }
        }

        #endregion

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the Loaded event for the Browser control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BrowserControl_Loaded(object sender, RoutedEventArgs e)
        {
            OnLoad();
        }

        /// <summary>
        /// Handles the Navigate event for the main menu control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainMenu_Navigate(object sender, SubMenuNavigationEventArgs e)
        {
            if (e.SubMenu != null)
                this.Navigate(e.SubMenu.ContentUri);
        }

        /// <summary>
        /// Handles the navigate to home event for the application map path control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ApplicationMapPath_NavigateToHome(object sender, HomeNavigationEventArgs e)
        {
            if (e.Home != null)
                this.Navigate(e.Home.ContentUri);
        }

        /// <summary>
        /// Handles the navigate to main menu event for the application map path control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ApplicationMapPath_NavigateToMainMenu(object sender, MainMenuNavigationEventArgs e)
        {
            if (e.MainMenu != null)
            {
                SubMenu subMenu = new SubMenu(e.MainMenu.SubMenus);
                subMenu.Title = e.MainMenu.Title;
                subMenu.Closed += new EventHandler(SubMenu_Closed);
                subMenu.Show();
            }
        }

        /// <summary>
        /// Handles the Navigate to sub menu event for the application map path control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ApplicationMapPath_NavigateToSubMenu(object sender, SubMenuNavigationEventArgs e)
        {
            if (e.SubMenu != null)
            {
                this.Navigate(e.SubMenu.ContentUri);
            }
        }

        /// <summary>
        /// Handles the closed event of the sub menu.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SubMenu_Closed(object sender, EventArgs e)
        {
            if (sender is SubMenu)
            {
                SubMenu subMenu = (SubMenu)sender;

                if (subMenu.DialogResult.HasValue && subMenu.DialogResult.Value && subMenu.SelectedSubMenuItem != null)
                {
                    this.Navigate(subMenu.SelectedSubMenuItem.ContentUri);
                    this.ApplicationMap.NavigateTo(subMenu.SelectedSubMenuItem);
                    this.ApplicationMapPath.Path = this.ApplicationMap.Path;
                    this.ApplicationMapPath.Visibility = Visibility.Visible;
                    this.ApplicationMapPathBorder.Visibility = Visibility.Visible;
                }
            }
        }

        /// <summary>
        /// Handles the GoBack event of the navigation toolbar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NavigationToolbar_GoBack(object sender, EventArgs e)
        {
            this.NavigationFrame.GoBack();
        }

        /// <summary>
        /// Handles the GoForward event of the navigation toolbar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NavigationToolbar_GoForward(object sender, EventArgs e)
        {
            this.NavigationFrame.GoForward();
        }

        /// <summary>
        /// Handles the Go event of the navigation toolbar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NavigationToolbar_Go(object sender, NavigationEventArgs e)
        {
            this.Navigate(e.Uri);
        }

        /// <summary>
        /// Handles the Navigated event of the frame.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NavigationFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            NavigationToolbar.CanGoBack = NavigationFrame.CanGoBack;
            NavigationToolbar.CanGoForward = NavigationFrame.CanGoForward;

            //TODO: sync application map
            MenuItem menuItem = this.ApplicationMap.FindMenuItemByUri(this.NavigationFrame.CurrentSource.ToString());

            if (menuItem != null)
            {
                if (menuItem is HomeMenuItem)
                {
                    this.ApplicationMapPath.Visibility = Visibility.Collapsed;
                    this.ApplicationMapPathBorder.Visibility = Visibility.Collapsed;
                    this.ApplicationMapPath.Path = null;
                }
                else if (menuItem is SubMenuItem)
                {
                    this.ApplicationMap.NavigateTo((SubMenuItem)menuItem);
                    this.ApplicationMapPath.Path = this.ApplicationMap.Path;
                    this.ApplicationMapPath.Visibility = Visibility.Visible;
                    this.ApplicationMapPathBorder.Visibility = Visibility.Visible;
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Occurs when the control is loaded.
        /// </summary>
        /// <remarks>
        /// This method calls methods to configure different portions of the browser and navigates to
        /// the home uri, if specified, in the application map.
        /// </remarks>
        private void OnLoad()
        {
            ConfigureNavigationToolbar();
            ConfigureApplicationPath();
            ConfigureMenu();

            if (HistoryManager != null)
                HistoryManager.Load();

            if (FavoritesManager != null)
                FavoritesManager.Load();

            if(this.FavoritesManager != null && this.FavoritesManager.HomePage != null &&
                !string.IsNullOrEmpty(this.FavoritesManager.HomePage.Uri))
            {
                this.Navigate(this.FavoritesManager.HomePage.Uri);
                this.ApplicationMapPath.Visibility = Visibility.Collapsed;
                this.ApplicationMapPathBorder.Visibility = Visibility.Collapsed;
                this.ApplicationMapPath.Path = null;
            }
            else if (this.ApplicationMap != null && this.ApplicationMap.HomeMenuItem != null &&
                !string.IsNullOrEmpty(this.ApplicationMap.HomeMenuItem.ContentUri))
            {
                this.Navigate(this.ApplicationMap.HomeMenuItem.ContentUri);
                this.ApplicationMapPath.Visibility = Visibility.Collapsed;
                this.ApplicationMapPathBorder.Visibility = Visibility.Collapsed;
                this.ApplicationMapPath.Path = null;
            }
        }

        /// <summary>
        /// Configures the navigation toolbar.
        /// </summary>
        /// <remarks>
        /// This method does the following:
        /// 1. Sets the IsExpanded property of the navigation toolbar based upon the IsNavigationToolbarExpanded property.
        /// 2. Sets the Visiblity of the navigation toolbar based upon the DisplayNavigationToolbar property.
        /// 3. Sets the Application
        /// </remarks>
        private void ConfigureNavigationToolbar()
        {
            this.NavigationToolbar.IsExpanded = this.IsNavigationToolbarExpanded;
            this.NavigationToolbar.Visibility = GetNavigationVisiblity(DisplayNavigationToolbar);
            this.NavigationToolbarBorder.Visibility = GetNavigationVisiblity(DisplayNavigationToolbar);
        }

        /// <summary>
        /// Configures the application path.
        /// </summary>
        /// <remarks>
        /// This method sets the visiblity of the application path based upon the DisplayApplicationPath
        /// property.
        /// </remarks>
        private void ConfigureApplicationPath()
        {
            this.ApplicationMapPath.Visibility = GetNavigationVisiblity(DisplayApplicationPath);
            this.ApplicationMapPathBorder.Visibility = GetNavigationVisiblity(DisplayApplicationPath);
        }

        /// <summary>
        /// Configures the menu.
        /// </summary>
        /// <remarks>
        /// This method sets the visiblity of the menu based upon the DisplayMenu property and
        /// sets the nodes of the menu to the nodes of the browser's application map.
        /// </remarks>
        private void ConfigureMenu()
        {
            this.MainMenu.Visibility = GetNavigationVisiblity(DisplayMenu);
            this.MenuBorder.Visibility = GetNavigationVisiblity(DisplayMenu);

            if (this.ApplicationMap != null)
                this.MainMenu.MainMenuItems = this.ApplicationMap.MainMenuItems;
        }

        /// <summary>
        /// Determins visibility based on the DisplayNaviagtionToolbar property and
        /// the current browser state (i.e. in browser or out of browser).
        /// </summary>
        /// <param name="displayNavigationItem">The DisplayNavigationItem enumerated value to use when determining visibility.</param>
        /// <returns></returns>
        private Visibility GetNavigationVisiblity(DisplayNavigationItem displayNavigationItem)
        {
            Visibility navigationVisibility = Visibility.Visible;

            switch (displayNavigationItem)
            {
                case DisplayNavigationItem.Always:
                    ///Always display the NavigationToolbar control.
                    navigationVisibility = Visibility.Visible;
                    break;

                case DisplayNavigationItem.InBrowser:
                    ///Only display the NavigationToolbar control if the application is running
                    ///in the browser.
                    if (App.Current.RunningOffline)
                        navigationVisibility = Visibility.Collapsed;
                    else
                        navigationVisibility = Visibility.Visible;
                    break;

                case DisplayNavigationItem.Never:
                    ///Never display the NavigationToolbar control.
                    navigationVisibility = Visibility.Collapsed;
                    break;

                case DisplayNavigationItem.OutOfBrowser:
                    ///Only display the NavigationToolbar control if the application is running
                    ///out of the browser.
                    if (App.Current.RunningOffline)
                        navigationVisibility = Visibility.Visible;
                    else
                        navigationVisibility = Visibility.Collapsed;
                    break;
            }

            return navigationVisibility;
        }

        /// <summary>
        /// Navigates to the specified uri.
        /// </summary>
        /// <param name="uri">The uri to navigate to.</param>
        private void Navigate(string uri)
        {
            this.NavigationFrame.Navigate(new Uri(uri, UriKind.Relative));
            HistoryManager.Add(uri, DateTime.Now);
            HistoryManager.Save();
            this.NavigationToolbar.HistoryManager = HistoryManager;
            this.NavigationToolbar.CurrentUri = uri;
        }

        #endregion
    }
}